#include "Camera.h"

Camera& GetCamera()
{
	static Camera camera;
	return camera;
}

Camera::Camera()
{
	mPosition = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
	mRight    = XMVectorSet(1.0f, 0.0f, 0.0f, 1.0f);
	mUp       = XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f);
	mLook     = XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f);

	mView = XMMatrixIdentity();
	mProj = XMMatrixIdentity();
}

Camera::~Camera()
{
}

XMVECTOR& Camera::Position()
{
	return mPosition;
}

XMMATRIX Camera::View()const
{
	return mView;
}

XMMATRIX Camera::Proj()const
{
	return mProj;
}

void Camera::SetPerspective(FLOAT fovY, FLOAT aspect, FLOAT zn, FLOAT zf)
{
	mProj = XMMatrixPerspectiveFovLH(fovY, aspect, zn, zf);
}

void Camera::SetOrthographic(FLOAT viewWidth, FLOAT viewHeight, FLOAT zn, FLOAT zf)
{
	mProj = XMMatrixOrthographicLH(viewWidth, viewHeight, zn, zf);
}

void Camera::LookAt(const XMVECTOR& target)
{
	mLook = target - mPosition;
}

void Camera::SetPosition(const XMVECTOR& position)
{
	mPosition = position;
}

void Camera::SetUp(const XMVECTOR& up)
{
	mUp = up;
}

void Camera::Strafe(FLOAT distance)
{
	mPosition += distance * mRight;
}

void Camera::Walk(FLOAT distance)
{
	mPosition += distance * mLook;
}

void Camera::Pitch(FLOAT angle)
{
	XMMATRIX R = XMMatrixRotationAxis(mRight, angle);
	mUp = XMVector3TransformNormal(mUp, R);
	mLook = XMVector3TransformNormal(mLook, R);
}

void Camera::RotateY(FLOAT angle)
{
	XMMATRIX R = XMMatrixRotationY(angle);

	mRight = XMVector3TransformNormal(mRight, R);
	mUp = XMVector3TransformNormal(mUp, R);
	mLook = XMVector3TransformNormal(mLook, R);
}

void Camera::RebuildView()
{
	// Keep camera's axes orthogonal to each other and of unit length.
	mLook = XMVector3Normalize(mLook);
	
	mRight = XMVector3Cross(mUp, mLook);
	mRight = XMVector3Normalize(mRight);

	mUp = XMVector3Cross(mLook, mRight);
	mUp = XMVector3Normalize(mUp);

	// The matrix from camera space to world space would be easy (camera vectors on rows), but we 
	// actually need its inverse (from world space to camera space). This matrix is obtained by the
	// the following formulas

	FLOAT x = -XMVectorGetX(XMVector3Dot(mPosition, mRight));
	FLOAT y = -XMVectorGetX(XMVector3Dot(mPosition, mUp));
	FLOAT z = -XMVectorGetX(XMVector3Dot(mPosition, mLook));

	mView(0,0) = XMVectorGetX(mRight); 
	mView(1,0) = XMVectorGetY(mRight); 
	mView(2,0) = XMVectorGetZ(mRight); 
	mView(3,0) = x;   

	mView(0,1) = XMVectorGetX(mUp);
	mView(1,1) = XMVectorGetY(mUp);
	mView(2,1) = XMVectorGetZ(mUp);
	mView(3,1) = y;  

	mView(0,2) = XMVectorGetX(mLook); 
	mView(1,2) = XMVectorGetY(mLook); 
	mView(2,2) = XMVectorGetZ(mLook); 
	mView(3,2) = z;   

	mView(0,3) = 0.0f;
	mView(1,3) = 0.0f;
	mView(2,3) = 0.0f;
	mView(3,3) = 1.0f;
}


